/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package spriteeditor.sprite;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Observable;
import javax.imageio.ImageIO;

/**
 *
 * @author sergey.ivchenko
 */
public class Sprite extends Observable
{
    public BufferedImage image;
    private BufferedImage scaledImage;
    private double lastPaintZoom;
    private String spriteFilePath;
    private int frameW = 32;
    private int frameH = 32;
    private int xIndex = 0;
    private int yIndex = 0;
    private int border = 0;
    private int margin = 0;

    private Animation curAnim;
    private Frame curFrame;

    private ArrayList <Animation> anims = new ArrayList<Animation>();
    private ArrayList <Frame> frames = new ArrayList<Frame>();

    public final static String REFRESH_FRAMES = "refreshFrames";
    public final static String REFRESH_ANIMS_PANEL = "refreshAnimsPanel";
    
    public Animation getCurAnim() {
        return curAnim;
    }

    public int getAnimsCount() {
        return anims.size();
    }

    public ArrayList <Animation> getAnims()
    {
        return anims;
    }

    public void setCurAnim(Animation curAnim)
    {
        this.curAnim = curAnim;
        curFrame = null;
        setChanged();
        notifyObservers(REFRESH_ANIMS_PANEL);
    }
    public void setCurAnim(int index)
    {
        curAnim = anims.get(index);
        curFrame = null;
        setChanged();
        notifyObservers(REFRESH_ANIMS_PANEL);
    }

    public Frame getCurFrame()
    {
        return curFrame;
    }

    public void setCurFrame(Frame curFrame)
    {
        this.curFrame = curFrame;
        setChanged();
        notifyObservers();
    }

    public ArrayList<Frame> getFrames() {
        return frames;
    }

    public int getBorder() {
        return border;
    }


    public void setBorder(int border) {
        this.border = border;
    }

    public void requestFramesRefresh ()
    {
        setChanged();
        notifyObservers(REFRESH_FRAMES);
    }
            

    public int getFrameH() {
        return frameH;
    }

    public void setFrameH(int frameH) {
        this.frameH = frameH;
    }

    public int getFrameW() {
        return frameW;
    }

    public void setFrameW(int frameW)
    {
        this.frameW = frameW;
    }

    public void currAnimAddFrame(Frame f)
    {
        if(curAnim == null)
            return;

        curAnim.addFrame(f);
        setChanged();
        notifyObservers(REFRESH_ANIMS_PANEL);
    }
    public void currAnimMoveFrame(int index, int dir)
    {
        if(curAnim == null || !curAnim.moveFrame(index, dir))
            return;
        setChanged();
        notifyObservers(REFRESH_ANIMS_PANEL);
    }

    public void currAnimRemoveFrame(int index)
    {
        if(curAnim == null || !curAnim.removeFrame(index))
            return;
        setChanged();
        notifyObservers(REFRESH_ANIMS_PANEL);
    }

    public int getMargin() {
        return margin;
    }

    public void setMargin(int margin) {
        this.margin = margin;
        setChanged();
        notifyObservers();
    }

    public int getxIndex() {
        return xIndex;
    }

    public void setxIndex(int xIndex) {
        this.xIndex = xIndex;
    }

    public int getyIndex() {
        return yIndex;
    }

    public void setyIndex(int yIndex) {
        this.yIndex = yIndex;
    }

    
    public void paintSpriteImage (Graphics g, int x, int y, double zoom)
    {
        if (image == null)
            return;
        if(zoom == 1.0)
        {
            g.drawImage(image, x, y, null);
        }
        else
        {
            if (lastPaintZoom != zoom)
            {
                AffineTransform tr = new AffineTransform();
                tr.scale(zoom, zoom);
                AffineTransformOp filter = new AffineTransformOp(tr, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
                scaledImage = filter.filter(image, null);
                lastPaintZoom = zoom;
            }
            g.drawImage(scaledImage, x, y, null);
        }
        if(frameW < 1|| frameH < 1)
            return;
        int imgW = image.getWidth();
        int imgH = image.getHeight();
        int rX = margin;
        int rY = margin;
        int xInd = 0;
        int yInd = 0;
        g.setColor(Color.RED);
        while (rY + frameH < imgH)
        {
            rX = margin;
            while (rX + frameW < imgW)
            {
                if(xInd >= xIndex && yInd >= yIndex || xInd <= xIndex && yInd > yIndex)
                {
                    int fX = (int) (rX * zoom + x);
                    int fY = (int) (rY * zoom + y);
                    int fW = (int) (frameW * zoom);
                    int fH = (int) (frameH * zoom);
                    g.drawRect(fX, fY, fW, fH);
                }
                xInd++;
                rX += frameW + border;
            }
            xInd = 0;
            yInd++;
            rY += frameH + border;
        }
    }
    
    public void addAnim (Animation a)
    {
        anims.add(a);
        curAnim = a;
        setChanged();
        notifyObservers(REFRESH_ANIMS_PANEL);
    }

    public void loadSpriteImage (File imageFile)
    {
        spriteFilePath = imageFile.getAbsolutePath() + ".spr";
        try
        {
            image = ImageIO.read(imageFile);
            setChanged();
            notifyObservers(REFRESH_FRAMES);
        }
        catch (Exception ex)
        {
            System.out.println("Error while loading sprite image");
            return;
        }
    }
    
    public void loadSpriteImage (String imageFileName)
    {
        File imageFile = new File(imageFileName);
        loadSpriteImage(imageFile);
    }

    public void saveSprite ()
    {
        FileOutputStream fout = null;
        try
        {
            File f = new File(spriteFilePath);
            fout = new FileOutputStream(f);

            DataOutputStream spriteStream = new DataOutputStream(fout);
            spriteStream.writeShort(frameW);
            spriteStream.writeShort(frameH);
            spriteStream.writeShort(xIndex);
            spriteStream.writeShort(yIndex);
            spriteStream.writeShort(border);
            spriteStream.writeShort(margin);
            spriteStream.writeByte(anims.size());
            for(Animation a : anims)
            {
                saveAnim(a, spriteStream);
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }

    private void saveAnim (Animation anim, DataOutputStream spriteStream) throws IOException
    {
        ArrayList <Frame> frames = anim.getFrames();
        spriteStream.writeShort(anim.getFramesCount());
        for(Frame f : frames)
        {
            spriteStream.writeShort(f.getXIndex());
            spriteStream.writeShort(f.getYindex());
        }
    }

    public void refreshFramesAndAnims ()
    {
        if(frameW < 1|| frameH < 1)
            return;
        frames.clear();
        int imgW = image.getWidth();
        int imgH = image.getHeight();
        int rX = margin;
        int rY = margin;
        int xInd = 0;
        int yInd = 0;
        int fIndex = 0;
        while (rY + frameH < imgH)
        {
            rX = margin;
            while (rX + frameW < imgW)
            {
                if(xInd >= xIndex && yInd >= yIndex || xInd <= xIndex && yInd > yIndex)
                {
                    BufferedImage frameImage = new BufferedImage(frameW, frameH,  BufferedImage.TYPE_INT_ARGB);
                    int rgb[] = image.getRGB(rX, rY, frameW, frameH, null, 0, frameW);
                    frameImage.setRGB(0, 0, frameW, frameH, rgb, 0, frameW);
                    frames.add(new Frame(fIndex, xInd, yInd, frameImage));
                    fIndex++;
                }
                xInd++;
                rX += frameW + border;
            }
            xInd = 0;
            yInd++;
            rY += frameH + border;
        }

        for (Animation a : anims)
        {
            int framesCount = a.getFramesCount();
            for(int i = 0; i < framesCount; i++)
            {
                int frameId = a.getFrame(i).getId();
                Frame newFrame = frames.get(frameId);
                a.setFrame(frameId, newFrame);
            }
        }
    }

    public static Sprite LoadSprite (File f)
    {
        Sprite s = null;
        try
        {
            FileInputStream fin = new FileInputStream(f);
            DataInputStream spriteStream = new DataInputStream(fin);
            s = new Sprite();
            s.setFrameW(spriteStream.readShort());
            s.setFrameH(spriteStream.readShort());
            s.setxIndex(spriteStream.readShort());
            s.setyIndex(spriteStream.readShort());
            s.setBorder(spriteStream.readShort());
            s.setMargin(spriteStream.readShort());
            s.spriteFilePath = f.getAbsolutePath();
            String imageFilePath = s.spriteFilePath.substring(0,s.spriteFilePath.lastIndexOf("."));
            s.loadSpriteImage(imageFilePath);
            s.refreshFramesAndAnims();

            int animsCount = spriteStream.readByte();

            for(int i = 0; i < animsCount; i++)
            {
                s.anims.add(s.loadAnim(spriteStream));
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return s;
    }

    private Frame getFrameByXY(int x, int y)
    {
        for(Frame f: frames)
        {
            if(f.getXIndex() == x && f.getYindex() == y)
                return f;
        }
        return null;
    }
    private Animation loadAnim (DataInputStream spriteStream) throws IOException
    {
        Animation anim = new Animation();
        int framesCount = spriteStream.readShort();
        for(int i = 0; i < framesCount; i++)
        {
            int fX = spriteStream.readShort();
            int fY = spriteStream.readShort();
            Frame f = getFrameByXY(fX, fY);
            anim.addFrame(f);
        }
        return anim;
    }
}
